import { DeviceInputKey, InputStatus } from './device-information';
import { Scene } from '@babylonjs/core/scene';
import { CreateHandleTo, IHandle, RemoveHandle } from '../../data/data-handle';
import { Vector3 } from '@babylonjs/core/Maths/math.vector';
import { PointerInfo } from '@babylonjs/core/Events/pointerEvents';
import { AnimationFrame } from '../../animation/animation-frame';



interface IInputPointerStatusOptions {
    pressThreshold?: number,
    staticThreshold?: number,
}


class InputPointerStatus {

    private _options: IInputPointerStatusOptions;
    private _time: number = 0;
    private _duration: number = 0;
    private _cocurrent: number = 1;
    private _status: InputStatus = InputStatus.none;
    private _downHandles: IHandle<PointerInfo>[] = [];
    private _clickHandles: IHandle<PointerInfo>[] = [];
    private _pressHandles: IHandle<PointerInfo>[] = [];
    private _upHandles: IHandle<PointerInfo>[] = [];
    private _moveHandles: IHandle<PointerInfo>[] = [];
    private _wheelHandles: IHandle<PointerInfo>[] = [];
    private _pointerHandle: { (e: PointerInfo): void };
    private _observing = false;
    private _req = {};
    private _dirStart = new Vector3();
    private _dirEnd = new Vector3();

    public readonly key: DeviceInputKey;
    public readonly scene: Scene;

    constructor(key: DeviceInputKey, scene: Scene, options?: IInputPointerStatusOptions) {
        this.key = key;
        this.scene = scene;
        const ops = options || {};
        ops.pressThreshold = ops.pressThreshold || 500;
        ops.staticThreshold = ops.staticThreshold || 0.998;
        this._options = ops;

        this._pointerHandle = (e: PointerInfo) => {
            if (!e.pickInfo) return;
            if (!e.pickInfo.ray) return;
            switch (e.event.type) {
                case 'pointerdown':
                    AnimationFrame.cancel(this._req);
                    this._duration = 0;
                    this._cocurrent = 1;
                    this._time = performance.now();
                    this._dirStart.copyFrom(e.pickInfo.ray.direction);
                    this._status = InputStatus.down;
                    this._downHandles.forEach(h => h.callback(e));
                    AnimationFrame.throttleByTime(this._req, this._options.pressThreshold!, () => {
                        this._duration = performance.now() - this._time;
                        if ((this._duration > this._options.pressThreshold!) && (this._cocurrent > this._options.staticThreshold!)) {
                            this._status = InputStatus.press;
                            this._pressHandles.forEach(h => h.callback(e));
                        }
                        AnimationFrame.cancel(this._req);
                    }, false);
                    break;
                case 'pointermove':
                    this._duration = performance.now() - this._time;
                    this._dirEnd.copyFrom(e.pickInfo.ray.direction);
                    this._cocurrent = Vector3.Dot(this._dirStart, this._dirEnd);
                    this._status = InputStatus.move;
                    this._moveHandles.forEach(h => h.callback(e));
                    break;
                case 'pointerup':
                    if ((this._duration < this._options.pressThreshold!) && (this._cocurrent > this._options.staticThreshold!)) {
                        this._status = InputStatus.click;
                        this._clickHandles.forEach(h => h.callback(e));
                    }
                    this._status = InputStatus.up;
                    this._upHandles.forEach(h => h.callback(e));
                    AnimationFrame.cancel(this._req);
                    break;
                case 'wheel':
                    this._status = InputStatus.wheel;
                    this._wheelHandles.forEach(h => h.callback(e));
                    break;
            }
        };
    }

    /**
     * 获取当前状态
     */
    public get status(): InputStatus {
        return this._status;
    }

    /**
     * 是否为空闲状态（没有响应的回调函数）
     */
    public get isIdle(): boolean {
        return !(this._downHandles.length || this._clickHandles.length || this._pressHandles.length || this._upHandles.length || this._moveHandles.length);
    }

    /**
     * 添加移动时的回调函数对象
     * @param callback 回调函数
     * @returns 回调函数对象
     */
    public onMove(callback: { (p: PointerInfo): void }): IHandle<PointerInfo> {
        return CreateHandleTo<PointerInfo>(callback, this._moveHandles);
    }

    /**
     * 移除移动时的回调函数对象
     * @param handle 回调函数对象或其id
     */
    public removeMoveHandle(handle: IHandle<PointerInfo> | number): void {
        RemoveHandle(handle, this._moveHandles);
    }

    /**
     * 添加滚动时的回调函数对象
     * @param callback 回调函数
     * @returns 回调函数对象
     */
    public onWheel(callback: { (p: PointerInfo): void }): IHandle<PointerInfo> {
        return CreateHandleTo<PointerInfo>(callback, this._wheelHandles);
    }

    /**
     * 移除滚动时的回调函数对象
     * @param handle 回调函数对象或其id
     */
    public removeWheelHandle(handle: IHandle<PointerInfo> | number): void {
        RemoveHandle(handle, this._wheelHandles);
    }

    /**
     * 添加按下时的回调函数对象
     * @param callback 回调函数
     * @returns 回调函数对象
     */
    public onDown(callback: { (p: PointerInfo): void }): IHandle<PointerInfo> {
        return CreateHandleTo<PointerInfo>(callback, this._downHandles);
    }

    /**
     * 移除按下时的回调函数对象
     * @param handle 回调函数对象或其id
     */
    public removeDownHandle(handle: IHandle<PointerInfo> | number): void {
        RemoveHandle(handle, this._downHandles);
    }

    /**
     * 添加点击时的回调函数对象
     * @param callback 回调函数
     * @returns 回调函数对象
     */
    public onClick(callback: { (p: PointerInfo): void }): IHandle<PointerInfo> {
        return CreateHandleTo<PointerInfo>(callback, this._clickHandles);
    }

    /**
     * 移除点击时的回调函数对象
     * @param handle 回调函数对象或其id
     */
    public removeClickHandle(handle: IHandle<PointerInfo> | number): void {
        RemoveHandle(handle, this._clickHandles);
    }

    /**
     * 添加按住时的回调函数对象
     * @param callback 回调函数
     * @returns 回调函数对象
     */
    public onPress(callback: { (p: PointerInfo): void }): IHandle<PointerInfo> {
        return CreateHandleTo<PointerInfo>(callback, this._pressHandles);
    }

    /**
     * 移除按住时的回调函数对象
     * @param handle 回调函数对象或其id
     */
    public removePressHandle(handle: IHandle<PointerInfo> | number): void {
        RemoveHandle(handle, this._pressHandles);
    }

    /**
     * 添加抬起时的回调函数对象
     * @param callback 回调函数
     * @returns 回调函数对象
     */
    public onUp(callback: { (p: PointerInfo): void }): IHandle<PointerInfo> {
        return CreateHandleTo<PointerInfo>(callback, this._upHandles);
    }

    /**
     * 移除抬起时的回调函数对象
     * @param handle 回调函数对象或其id
     */
    public removeUpHandle(handle: IHandle<PointerInfo> | number): void {
        RemoveHandle(handle, this._upHandles);
    }

    /**
    * 开始检测输入
    */
    public check(): void {
        if (this.isIdle) {
            this.scene.onPointerObservable.removeCallback(this._pointerHandle);
            this._observing = false;
            return;
        }
        if (this._observing) return;
        this.scene.onPointerObservable.add(this._pointerHandle);
        this._observing = true;
    }

    /**
     * 重置输入状态
     */
    public reset(): void {
        this._status = InputStatus.none;
        this._time = 0;
        this._duration = 0;
    }

}


export { InputPointerStatus };